﻿using Stardust.Common.Model;
using Stardust.Common.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Stardust.Server
{
    class ActionManager
    {
        private static Type serviceType = typeof(IStardustService);

        public static Dictionary<string, ActionWrapper> actionDict;


        public static List<ServiceContractModel> LoadActions(ServerNodeModel node)
        {
            List<ServiceContractModel> contractList = new List<ServiceContractModel>();
            actionDict = new Dictionary<string, ActionWrapper>();
            foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                foreach (var type in assembly.GetTypes())
                {
                    if (serviceType.IsAssignableFrom(type))
                    {
                        string controllerName = null;
                        string controllerDesc = null;
                        var clsAttributes = type.GetCustomAttributes(StardustNameAttribute.ThisType, false);
                        var serviceNameAttribute = clsAttributes.FirstOrDefault() as StardustNameAttribute;
                        if (serviceNameAttribute != null)
                        {
                            controllerName = serviceNameAttribute.Name;
                            controllerDesc = serviceNameAttribute.Description ?? string.Empty;
                        }
                        if (string.IsNullOrEmpty(controllerName))
                        {
                            if (type.Name.EndsWith("Service", StringComparison.OrdinalIgnoreCase))
                            {
                                controllerName = type.Name.Remove(type.Name.Length - 7, 7);
                            }
                            else
                            {
                                controllerName = type.Name;
                            }
                        }

                        var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
                        foreach (var method in methods)
                        {
                            if (method.ReturnType == typeof(void)) continue; //必须具有返回值
                            var methodAttributes = method.GetCustomAttributes(StardustNameAttribute.ThisType, false);
                            var methodNameAttr = methodAttributes.FirstOrDefault() as StardustNameAttribute;
                            string actionName = null;
                            string actionDesc = null;

                            if (methodNameAttr != null)
                            {
                                actionName = methodNameAttr.Name;
                                actionDesc = methodNameAttr.Description ?? string.Empty;
                            }
                            if (string.IsNullOrEmpty(actionName))
                            {
                                actionName = method.Name;
                            }
                            //契约
                            if (!controllerName.Equals("__Internal", StringComparison.OrdinalIgnoreCase))
                            {
                                var contract = BuildContract(node, type, method, controllerName, controllerDesc, actionName, actionDesc);
                                contractList.Add(contract);
                            }

                            var actionWrapper = new ActionWrapper("/" + controllerName + "/" + actionName, type, method);
                            actionDict[actionWrapper.Path.ToUpper()] = actionWrapper;
                        }
                    }
                }
            }
            return contractList;
        }

        public static ActionWrapper GetActionWrapper(string path)
        {
            ActionWrapper wrapper = null;
            if (actionDict.TryGetValue(path.ToUpper(), out wrapper))
            {
                return wrapper;
            }
            else
            {
                return null;//或default
            }
        }

        private static ServiceContractModel BuildContract(ServerNodeModel node, Type type, MethodInfo method, string controllerName, string controllerDesc, string actionName, string actionDesc)
        {
            ServiceContractModel contract = new ServiceContractModel();
            contract.ServerNodeId = node.Id;
            contract.ControllerName = controllerName;
            contract.ActionName = actionName;
            contract.ControllerDescription = controllerDesc;
            contract.ActionDescription = actionDesc;

            //参数JSON
            ObjectGenerator paramsGenerator = new ObjectGenerator();
            var Parameters = method.GetParameters();

            //类
            if (Parameters.Length == 1 && !TypeHelper.IsSimpleType(Parameters[0].ParameterType))
            {
                var paramsObject = paramsGenerator.GenerateObject(Parameters[0].ParameterType);
                contract.Params = SerializerHelper.SerializeToString(paramsObject).IndentJson();
            }
            else
            {
                //简单类型
                Dictionary<string, object> dictObject = new Dictionary<string, object>();
                for (int i = 0; i < Parameters.Length; i++)
                {
                    var p = Parameters[i];
                    dictObject[p.Name] = paramsGenerator.GenerateObject(p.ParameterType);
                }
                contract.Params = SerializerHelper.SerializeToString(dictObject).IndentJson();
            }
            //返回值JSON
            ObjectGenerator resultGenerator = new ObjectGenerator();
            if (method.ReturnType.Name == "Task`1")
            {
                var returnType = method.ReturnType.GetGenericArguments()[0];
                var resultObject = resultGenerator.GenerateObject(returnType);
                contract.Result = SerializerHelper.SerializeToString(resultObject).IndentJson();
            }
            else
            {
                var resultObject = resultGenerator.GenerateObject(method.ReturnType);
                contract.Result = SerializerHelper.SerializeToString(resultObject).IndentJson();
            }
            
            return contract;
        }

    }

    class ActionWrapper
    {
        ParameterInfo[] Parameters;
        public bool IsAsync
        {
            private set;
            get;
        }

        public ActionWrapper(string path, Type type, MethodInfo methodInfo)
        {
            Parameters = methodInfo.GetParameters();
            Path = path;
            _createObjFun = DynamicMethodHelper.BuildConstructorInvoker(type.GetConstructor(Type.EmptyTypes));
            _action = DynamicMethodHelper.BuilderMethodInvoker(methodInfo);
            if (methodInfo.ReturnType.Name == "Task`1")
            {
                IsAsync = true;
                _actionTask = DynamicMethodHelper.BuildContinueTaskInvoker(methodInfo.ReturnType);
            }
        }

        Func<object, object[], object> _action;
        Func<object> _createObjFun;
        Func<object, Task<object>> _actionTask;


        public string Path { get; set; }

        public object Invoke(string json)
        {
            var obj = _createObjFun();
            var ps = FatchParams(json);
            var result = _action(obj, ps);
            if (IsAsync)
            {
                var task = _actionTask(result);
                return task;
            }
            return result;
        }

        private object[] FatchParams(string json)
        {
            if (Parameters.Length == 0)
            {
                return new object[] { };
            }
            ////类
            if (Parameters.Length == 1 && !TypeHelper.IsSimpleType(Parameters[0].ParameterType))
            {
                var arg0 = SerializerHelper.DeserializeFromString(json, Parameters[0].ParameterType);
                return new object[] { arg0 };
            }

            //简单类型
            Dictionary<string, object> ignoreCaseDict = null;
            if (string.IsNullOrEmpty(json))
            {
                ignoreCaseDict = new Dictionary<string, object>();
            }
            else
            {
                var dict = SerializerHelper.DeserializeFromString(json, TypeHelper.Types.StringObjectDict) as Dictionary<string, object>;
                ignoreCaseDict = new Dictionary<string, object>(dict, StringComparer.OrdinalIgnoreCase);
            }
            var args = new object[Parameters.Length];

            for (int i = 0; i < Parameters.Length; i++)
            {
                var p = Parameters[i];
                object value;
                ignoreCaseDict.TryGetValue(p.Name, out value);
                if (value == null)
                {
                    value = p.DefaultValue is DBNull ? null : p.DefaultValue;
                }
                if (value != null)
                {
                    args[i] = Convert.ChangeType(value, p.ParameterType);
                }
                else
                {
                    args[i] = null;
                }
            }
            return args;
        }

    }
}
